{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import  tensorflow as tf\n",
    "from    tensorflow import keras\n",
    "from    tensorflow.keras import layers, Sequential\n",
    "\n",
    "\n",
    "\n",
    "class BasicBlock(layers.Layer):\n",
    "\n",
    "    def __init__(self, filter_num, stride=1):\n",
    "        super(BasicBlock, self).__init__()\n",
    "\n",
    "        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')\n",
    "        self.bn1 = layers.BatchNormalization()\n",
    "        self.relu = layers.Activation('relu')\n",
    "\n",
    "        self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')\n",
    "        self.bn2 = layers.BatchNormalization()\n",
    "\n",
    "        if stride != 1:\n",
    "            self.downsample = Sequential()\n",
    "            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))\n",
    "        else:\n",
    "            self.downsample = lambda x:x\n",
    "\n",
    "\n",
    "\n",
    "    def call(self, inputs, training=None):\n",
    "\n",
    "        # [b, h, w, c]\n",
    "        out = self.conv1(inputs)\n",
    "        out = self.bn1(out)\n",
    "        out = self.relu(out)\n",
    "\n",
    "        out = self.conv2(out)\n",
    "        out = self.bn2(out)\n",
    "\n",
    "        identity = self.downsample(inputs)\n",
    "\n",
    "        output = layers.add([out, identity])\n",
    "        output = tf.nn.relu(output)\n",
    "\n",
    "        return output\n",
    "\n",
    "\n",
    "class ResNet(keras.Model):\n",
    "\n",
    "\n",
    "    def __init__(self, layer_dims, num_classes=100): # [2, 2, 2, 2]\n",
    "        super(ResNet, self).__init__()\n",
    "\n",
    "        self.stem = Sequential([layers.Conv2D(64, (3, 3), strides=(1, 1)),\n",
    "                                layers.BatchNormalization(),\n",
    "                                layers.Activation('relu'),\n",
    "                                layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')\n",
    "                                ])\n",
    "\n",
    "        self.layer1 = self.build_resblock(64,  layer_dims[0])\n",
    "        self.layer2 = self.build_resblock(128, layer_dims[1], stride=2)\n",
    "        self.layer3 = self.build_resblock(256, layer_dims[2], stride=2)\n",
    "        self.layer4 = self.build_resblock(512, layer_dims[3], stride=2)\n",
    "\n",
    "        # output: [b, 512, h, w],\n",
    "        self.avgpool = layers.GlobalAveragePooling2D()\n",
    "        self.fc = layers.Dense(num_classes)\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "    def call(self, inputs, training=None):\n",
    "\n",
    "        x = self.stem(inputs)\n",
    "\n",
    "        x = self.layer1(x)\n",
    "        x = self.layer2(x)\n",
    "        x = self.layer3(x)\n",
    "        x = self.layer4(x)\n",
    "\n",
    "        # [b, c]\n",
    "        x = self.avgpool(x)\n",
    "        # [b, 100]\n",
    "        x = self.fc(x)\n",
    "\n",
    "        return x\n",
    "\n",
    "\n",
    "\n",
    "    def build_resblock(self, filter_num, blocks, stride=1):\n",
    "\n",
    "        res_blocks = Sequential()\n",
    "        # may down sample\n",
    "        res_blocks.add(BasicBlock(filter_num, stride))\n",
    "\n",
    "        for _ in range(1, blocks):\n",
    "            res_blocks.add(BasicBlock(filter_num, stride=1))\n",
    "\n",
    "        return res_blocks\n",
    "\n",
    "\n",
    "def resnet18():\n",
    "    return ResNet([2, 2, 2, 2])\n",
    "\n",
    "\n",
    "def resnet34():\n",
    "    return ResNet([3, 4, 6, 3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(50000, 32, 32, 3) (50000,) (10000, 32, 32, 3) (10000,)\n",
      "sample: (512, 32, 32, 3) (512,) tf.Tensor(-0.5, shape=(), dtype=float32) tf.Tensor(0.5, shape=(), dtype=float32)\n",
      "Model: \"res_net\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "sequential (Sequential)      multiple                  2048      \n",
      "_________________________________________________________________\n",
      "sequential_1 (Sequential)    multiple                  148736    \n",
      "_________________________________________________________________\n",
      "sequential_2 (Sequential)    multiple                  526976    \n",
      "_________________________________________________________________\n",
      "sequential_4 (Sequential)    multiple                  2102528   \n",
      "_________________________________________________________________\n",
      "sequential_6 (Sequential)    multiple                  8399360   \n",
      "_________________________________________________________________\n",
      "global_average_pooling2d (Gl multiple                  0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                multiple                  51300     \n",
      "=================================================================\n",
      "Total params: 11,230,948\n",
      "Trainable params: 11,223,140\n",
      "Non-trainable params: 7,808\n",
      "_________________________________________________________________\n",
      "0 0 loss: 4.605585098266602\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-2-4e3772e749d1>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     86\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'__main__'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 87\u001b[0;31m     \u001b[0mmain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     88\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-2-4e3772e749d1>\u001b[0m in \u001b[0;36mmain\u001b[0;34m()\u001b[0m\n\u001b[1;32m     56\u001b[0m                 \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreduce_mean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     57\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 58\u001b[0;31m             \u001b[0mgrads\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtape\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_variables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     59\u001b[0m             \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_gradients\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrads\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_variables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     60\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/eager/backprop.py\u001b[0m in \u001b[0;36mgradient\u001b[0;34m(self, target, sources, output_gradients, unconnected_gradients)\u001b[0m\n\u001b[1;32m   1012\u001b[0m         \u001b[0moutput_gradients\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0moutput_gradients\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1013\u001b[0m         \u001b[0msources_raw\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mflat_sources_raw\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1014\u001b[0;31m         unconnected_gradients=unconnected_gradients)\n\u001b[0m\u001b[1;32m   1015\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1016\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_persistent\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/eager/imperative_grad.py\u001b[0m in \u001b[0;36mimperative_grad\u001b[0;34m(tape, target, sources, output_gradients, sources_raw, unconnected_gradients)\u001b[0m\n\u001b[1;32m     74\u001b[0m       \u001b[0moutput_gradients\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     75\u001b[0m       \u001b[0msources_raw\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 76\u001b[0;31m       compat.as_str(unconnected_gradients.value))\n\u001b[0m",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/eager/backprop.py\u001b[0m in \u001b[0;36m_gradient_function\u001b[0;34m(op_name, attr_tuple, num_inputs, inputs, outputs, out_grads, skip_input_indices)\u001b[0m\n\u001b[1;32m    136\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mnum_inputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 138\u001b[0;31m   \u001b[0;32mreturn\u001b[0m \u001b[0mgrad_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmock_op\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mout_grads\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    139\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    140\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/ops/nn_grad.py\u001b[0m in \u001b[0;36m_FusedBatchNormV3Grad\u001b[0;34m(op, *grad)\u001b[0m\n\u001b[1;32m    924\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRegisterGradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"FusedBatchNormV3\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    925\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_FusedBatchNormV3Grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mgrad\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 926\u001b[0;31m   \u001b[0;32mreturn\u001b[0m \u001b[0m_BaseFusedBatchNormGrad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mgrad\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    927\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    928\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/ops/nn_grad.py\u001b[0m in \u001b[0;36m_BaseFusedBatchNormGrad\u001b[0;34m(op, version, *grad)\u001b[0m\n\u001b[1;32m    907\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mversion\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    908\u001b[0m       \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"reserve_space_3\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moutputs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 909\u001b[0;31m     \u001b[0mdx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdscale\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdoffset\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgrad_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    910\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mdata_format\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34mb\"NCHW\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    911\u001b[0m       \u001b[0mdx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray_ops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/opt/anaconda3/envs/tf20/lib/python3.7/site-packages/tensorflow_core/python/ops/gen_nn_ops.py\u001b[0m in \u001b[0;36mfused_batch_norm_grad_v3\u001b[0;34m(y_backprop, x, scale, reserve_space_1, reserve_space_2, reserve_space_3, epsilon, data_format, is_training, name)\u001b[0m\n\u001b[1;32m   4325\u001b[0m         \u001b[0my_backprop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscale\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreserve_space_1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreserve_space_2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   4326\u001b[0m         \u001b[0mreserve_space_3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"epsilon\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepsilon\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"data_format\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata_format\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 4327\u001b[0;31m         \"is_training\", is_training)\n\u001b[0m\u001b[1;32m   4328\u001b[0m       \u001b[0m_result\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_FusedBatchNormGradV3Output\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_make\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_result\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   4329\u001b[0m       \u001b[0;32mreturn\u001b[0m \u001b[0m_result\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "import  tensorflow as tf\n",
    "from    tensorflow.keras import layers, optimizers, datasets, Sequential\n",
    "import  os\n",
    "#from    resnet import resnet18\n",
    "\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL']='2'\n",
    "tf.random.set_seed(2345)\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "def preprocess(x, y):\n",
    "    # [-1~1]\n",
    "    x = tf.cast(x, dtype=tf.float32) / 255. - 0.5\n",
    "    y = tf.cast(y, dtype=tf.int32)\n",
    "    return x,y\n",
    "\n",
    "\n",
    "(x,y), (x_test, y_test) = datasets.cifar100.load_data()\n",
    "y = tf.squeeze(y, axis=1)\n",
    "y_test = tf.squeeze(y_test, axis=1)\n",
    "print(x.shape, y.shape, x_test.shape, y_test.shape)\n",
    "\n",
    "\n",
    "train_db = tf.data.Dataset.from_tensor_slices((x,y))\n",
    "train_db = train_db.shuffle(1000).map(preprocess).batch(512)\n",
    "\n",
    "test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))\n",
    "test_db = test_db.map(preprocess).batch(512)\n",
    "\n",
    "sample = next(iter(train_db))\n",
    "print('sample:', sample[0].shape, sample[1].shape,\n",
    "      tf.reduce_min(sample[0]), tf.reduce_max(sample[0]))\n",
    "\n",
    "\n",
    "def main():\n",
    "\n",
    "    # [b, 32, 32, 3] => [b, 1, 1, 512]\n",
    "    model = resnet18()\n",
    "    model.build(input_shape=(None, 32, 32, 3))\n",
    "    model.summary()\n",
    "    optimizer = optimizers.Adam(lr=1e-3)\n",
    "\n",
    "    for epoch in range(500):\n",
    "\n",
    "        for step, (x,y) in enumerate(train_db):\n",
    "\n",
    "            with tf.GradientTape() as tape:\n",
    "                # [b, 32, 32, 3] => [b, 100]\n",
    "                logits = model(x)\n",
    "                # [b] => [b, 100]\n",
    "                y_onehot = tf.one_hot(y, depth=100)\n",
    "                # compute loss\n",
    "                loss = tf.losses.categorical_crossentropy(y_onehot, logits, from_logits=True)\n",
    "                loss = tf.reduce_mean(loss)\n",
    "\n",
    "            grads = tape.gradient(loss, model.trainable_variables)\n",
    "            optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "\n",
    "            if step %50 == 0:\n",
    "                print(epoch, step, 'loss:', float(loss))\n",
    "\n",
    "\n",
    "\n",
    "        total_num = 0\n",
    "        total_correct = 0\n",
    "        for x,y in test_db:\n",
    "\n",
    "            logits = model(x)\n",
    "            prob = tf.nn.softmax(logits, axis=1)\n",
    "            pred = tf.argmax(prob, axis=1)\n",
    "            pred = tf.cast(pred, dtype=tf.int32)\n",
    "\n",
    "            correct = tf.cast(tf.equal(pred, y), dtype=tf.int32)\n",
    "            correct = tf.reduce_sum(correct)\n",
    "\n",
    "            total_num += x.shape[0]\n",
    "            total_correct += int(correct)\n",
    "\n",
    "        acc = total_correct / total_num\n",
    "        print(epoch, 'acc:', acc)\n",
    "\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
